import pandas as pd  
import glob  
import os  
import sys  
from datetime import datetime, time, timedelta
from datetime import datetime
import configparser  
import csv  
from openpyxl import load_workbook  
from openpyxl.styles import PatternFill  

# 假设的特定日期和时间（格式：年-月-日 时:分:秒）  
specific_datetime_str = "2024-09-01 14:30:00"
  
# 定义CSV文件的列名  
fieldnames = ['姓名', '出勤天数', '额定调休天数', '事假天数', '漏打卡次数', '上午迟到次数', '上午早退次数', '下午迟到次数', '下午早退次数']  
  

def check_attendance(times):  

    attendance_report= {'出勤天数': 0.0,'额定调休': 0.0,'事假天数': 0.0,'漏打卡': 0,'上午迟到': 0, '上午早退': 0, '下午迟到': 0, '下午早退': 0}   

    
    # 从配置文件中读取参数  
    
    #-------------------------------------------------上午打卡时间设置---------------------------------------------------------
    
    morning_clock_in = time(*map(int, config_ini['Morning']['clock_in'].split(':')))                                #上班打卡时间
    morning_clock_out = time(*map(int, config_ini['Morning']['clock_out'].split(':')))                              #下班打卡时间
    morning_clock_in_start_time = time(*map(int, config_ini['Morning']['clock_in_start_time'].split(':')))          #允许的最早上班打卡时间
    morning_clock_in_end_time = time(*map(int, config_ini['Morning']['clock_in_end_time'].split(':')))              #允许的最晚上班打卡时间
    morning_clock_out_start_time = time(*map(int, config_ini['Morning']['clock_out_start_time'].split(':')))        #允许的最早下班打卡时间
    morning_clock_out_end_timet = time(*map(int, config_ini['Morning']['clock_out_end_time'].split(':')))           #允许的最晚下班打卡时间

    #-------------------------------------------------下午打卡时间设置---------------------------------------------------------
    
    afternoon_clock_in = time(*map(int, config_ini['Afternoon']['clock_in'].split(':')))                                #上班打卡时间
    afternoon_clock_out = time(*map(int, config_ini['Afternoon']['clock_out'].split(':')))                              #下班打卡时间
    afternoon_clock_in_start_time = time(*map(int, config_ini['Afternoon']['clock_in_start_time'].split(':')))          #允许的最早上班打卡时间
    afternoon_clock_in_end_time = time(*map(int, config_ini['Afternoon']['clock_in_end_time'].split(':')))              #允许的最晚上班打卡时间
    afternoon_clock_out_start_time = time(*map(int, config_ini['Afternoon']['clock_out_start_time'].split(':')))        #允许的最早下班打卡时间
    afternoon_clock_out_end_timet = time(*map(int, config_ini['Afternoon']['clock_out_end_time'].split(':')))           #允许的最晚下班打卡时间
 
    
    # 初始化打卡记录  
    morning_start_punch     = None  
    morning_end_punch       = None  
    afternoon_start_punch   = None  
    afternoon_end_punch     = None 

    # 遍历打卡时间  
    for punch_time in times:  

        # 检查是否是上午上班打卡  
        if morning_clock_in_start_time  <= punch_time <= morning_clock_in_end_time :   # 允许提前、延时的 上班时间范围  使用最早的打卡记录 
            morning_start_punch = punch_time  
        # 检查是否是上午下班打卡  
        elif  morning_clock_out_start_time <= punch_time <= morning_clock_out_end_timet :   # 允许提前、延时的 下班时间范围 
            if morning_end_punch is None:
                morning_end_punch = punch_time 
            elif punch_time > morning_end_punch: #多次打卡，使用较晚的下班打卡记录
                morning_end_punch = punch_time             
            
        # 检查是否是下午上班打卡  
        if afternoon_start_punch is None and  afternoon_clock_in_start_time <= punch_time <= afternoon_clock_in_end_time:  # 允许提前、延时的 上班时间范围
            afternoon_start_punch = punch_time
        # 检查是否是下午下班打卡  
        elif  afternoon_clock_out_start_time <= punch_time <= afternoon_clock_out_end_timet:   #允许提前、延时的 下班时间范围 
            if afternoon_end_punch is None:
                afternoon_end_punch = punch_time 
            elif punch_time > afternoon_end_punch: #多次打卡，使用较晚的下班打卡记录
                afternoon_end_punch = punch_time      
          
    # 判断考勤状态  
    if morning_start_punch and morning_end_punch and afternoon_start_punch and afternoon_end_punch: 
        attendance_report['出勤天数']= 1   
        attendance_report['漏打卡']= 0   
        
        # 检查是否迟到或早退  
        if morning_start_punch > morning_clock_in:  
            attendance_report['上午迟到']= 1
        if morning_end_punch < morning_clock_out:  
            attendance_report['上午早退']= 1 
        if afternoon_start_punch > afternoon_clock_in:  
            attendance_report['下午迟到']= 1
        if afternoon_end_punch < afternoon_clock_out:  
            attendance_report['下午早退']= 1 
            
    elif morning_end_punch and afternoon_start_punch and afternoon_end_punch: #上午上班漏打卡 
        attendance_report['出勤天数']= 0.5    
        attendance_report['漏打卡']= 1  
        attendance_report['上午迟到']= 0      
        # 检查是否迟到或早退  

        if morning_end_punch < morning_clock_out:  
            attendance_report['上午早退']= 1 
        if afternoon_start_punch > afternoon_clock_in:  
            attendance_report['下午迟到']= 1
        if afternoon_end_punch < afternoon_clock_out:  
            attendance_report['下午早退']= 1 
            
    elif morning_start_punch and afternoon_start_punch and afternoon_end_punch: #上午下班漏打卡 
        attendance_report['出勤天数']= 0.5    
        attendance_report['漏打卡']= 1  
        attendance_report['上午早退']= 0      
        # 检查是否迟到或早退  

        if morning_start_punch > morning_clock_in:  
            attendance_report['上午迟到']= 1
        if afternoon_start_punch > afternoon_clock_in:  
            attendance_report['下午迟到']= 1
        if afternoon_end_punch < afternoon_clock_out:  
            attendance_report['下午早退']= 1     
            
    elif morning_start_punch and morning_end_punch and afternoon_start_punch: #下午下班漏打卡 
        attendance_report['出勤天数']= 0.5  
        attendance_report['漏打卡']= 1   
        attendance_report['下午早退']= 0 
        
        # 检查是否迟到或早退  
        if morning_start_punch > morning_clock_in:  
            attendance_report['上午迟到']= 1
        if morning_end_punch < morning_clock_out:  
            attendance_report['上午早退']= 1 
        if afternoon_start_punch > afternoon_clock_in:  
            attendance_report['下午迟到']= 1


    elif morning_start_punch and morning_end_punch and afternoon_end_punch: #下午上班漏打卡 
        attendance_report['出勤天数']= 0.5    
        attendance_report['漏打卡']= 1  
        attendance_report['下午迟到']= 0
        # 检查是否迟到或早退  
        if morning_start_punch > morning_clock_in:  
            attendance_report['上午迟到']= 1
        if morning_end_punch < morning_clock_out:  
            attendance_report['上午早退']= 1 
        if afternoon_end_punch < afternoon_clock_out:  
            attendance_report['下午早退']= 1
            
    elif afternoon_start_punch and afternoon_end_punch: #下午打满卡 上午未打卡 
        attendance_report['出勤天数']= 0.5    
        attendance_report['漏打卡']= 0  
        attendance_report['上午迟到']= 0     
        attendance_report['上午早退']= 0         
        # 检查是否迟到或早退  
 
        if afternoon_start_punch > afternoon_clock_in:  
            attendance_report['下午迟到']= 1
        if afternoon_end_punch < afternoon_clock_out:  
            attendance_report['下午早退']= 1 
        
    elif morning_start_punch and morning_end_punch : #上午打满卡 下午未打卡 
        attendance_report['出勤天数']= 0.5  
        attendance_report['漏打卡']= 0   
        attendance_report['下午早退']= 0 
        attendance_report['下午迟到']= 0 
        # 检查是否迟到或早退  
        if morning_start_punch > morning_clock_in:  
            attendance_report['上午迟到']= 1
        if morning_end_punch < morning_clock_out:  
            attendance_report['上午早退']= 1 
    elif morning_start_punch:                        #只有早上上班打卡
        attendance_report['出勤天数']= 0.0  
        attendance_report['漏打卡']= 1   
        attendance_report['上午早退']= 0  
        attendance_report['下午早退']= 0 
        attendance_report['下午迟到']= 0   
          # 检查是否迟到或早退  
        if morning_start_punch > morning_clock_in:  
            attendance_report['上午迟到']= 1
            
    elif morning_end_punch:                        #只有早上下班打卡
        attendance_report['出勤天数']= 0.0  
        attendance_report['漏打卡']= 1   
        attendance_report['上午迟到']= 0  
        attendance_report['下午早退']= 0 
        attendance_report['下午迟到']= 0   
          # 检查是否迟到或早退  
        if morning_end_punch < morning_clock_out:  
            attendance_report['上午早退']= 1 
            
    elif afternoon_start_punch:                        #只有下午上班打卡
        attendance_report['出勤天数']= 0.0  
        attendance_report['漏打卡']= 1   
        attendance_report['上午迟到']= 0  
        attendance_report['上午早退']= 0   
        attendance_report['下午早退']= 0    
          # 检查是否迟到或早退  
        if afternoon_start_punch > afternoon_clock_in:  
            attendance_report['下午迟到']= 1   
            
    elif afternoon_end_punch:                        #只有下午下班打卡
        attendance_report['出勤天数']= 0.0  
        attendance_report['漏打卡']= 1   
        attendance_report['上午迟到']= 0  
        attendance_report['上午早退']= 0   
        attendance_report['下午迟到']= 0    
          # 检查是否迟到或早退  
        if afternoon_end_punch < afternoon_clock_out:  
            attendance_report['下午早退']= 1   
            
    return attendance_report
         
         
# -------------------------------------------------------程序开始位置---------------------------------------------------------------------

# 开始运行              
print("开始分析 !") 
# 定义文件模式   
file_pattern = '*签卡记录*.xlsx'  
# 使用glob找到所有匹配的文件  
files = glob.glob(file_pattern)  
# 如果没有找到文件，则退出  
if not files:  
    print("没有找到包含'签卡记录'字符的.xlsx文件，请确认考勤表名称是' xxxx 签卡记录2024-06-01到2024-06-30 xxxx' 格式'  ")  
    input("按下 任意键退出...")
    exit()  
# 如果你想加载最新的文件，可以按文件名排序（假设文件名包含日期信息）  
# 注意：这个排序方法依赖于文件名中的日期格式，你可能需要根据你的文件名格式进行调整  
files.sort(key=os.path.getmtime, reverse=True)  # 或者使用 os.path.getctime 取决于你的需求  
  
# 加载最新的文件  
latest_file = files[0]  
file_name = os.path.basename(latest_file)  
print(f"加载文件：{latest_file}") 

# 使用openpyxl加载同一个Excel文件为工作簿对象  
wb = load_workbook(latest_file)  
ws = wb.active  # 获取活动工作表 
 
# 假设数据从第一行开始  
df = pd.read_excel(latest_file, header=0, engine='openpyxl')  


# 提取姓名和日期列  
# 找到“到”字的位置  
index_to   = file_name.find("到") 
# 确保我们找到了“到”  
if index_to != -1:  
    # 找到“签卡记录”后的位置（即日期开始的位置）  
    # 注意：这里我们假设“签卡记录”和日期之间没有空格或其他字符  
    index_sign_in_record = file_name.find("签卡记录") + 4  # 4 是“签卡记录”的长度  
    # 提取日期字符串  
    # 我们假设日期格式是YYYY-MM-DD，所以我们需要提取MM-DD部分  
    # 这里使用切片操作来获取从index_sign_in_record到index_to之前的字符串，  
    # 然后再次切片来只保留月和日  
    month = file_name[index_sign_in_record:index_to].split("-")[1]  # 按照“-”分割字符串"2024" "06" "01" ，并取第二部分（即06）   
    month_without_zero = int(month)  # 转换为整数，去除前导零  "6"
    month_without_zero_str = str(month_without_zero) + '-'  # 如果需要字符串形式"6-" 
else:  
    print("文件名不规范，名称应为' xxxx 签卡记录2024-06-01到2024-06-30 xxxx'格式")  
    input("按下 任意键退出...")
    exit()  
#“姓名”所在列的 所有的姓名行 按照“6-”日期格式前缀查找对应列
names = df['姓名'].unique()  
date_columns = [col for col in df.columns if col.startswith(month_without_zero_str)] 
#本月天数 
days_columns = len(date_columns)  
print(f"文件为-{month_without_zero_str}月份考勤,共：{days_columns}天") 
 
#加载外部文件
print("加载打卡时间标准配置文件...")  
# 初始化ConfigParser对象  
config_ini = configparser.ConfigParser()  
# 尝试读取配置文件  
try:  
    with open('work_hours.ini', 'r', encoding='utf-8') as configfile:  
        config_ini.read_file(configfile)  # 注意：在Python 3.8+中，可以使用read_file方法从文件对象读取  
        # 对于Python 3.8之前的版本，你需要使用config.read('work_hours.ini')  
except FileNotFoundError:  
    print("配置文件'work_hours.ini'不存在。")  
    input("按下 任意键退出...")
    exit()  
except configparser.Error as e:  
    print(f"配置文件'work_hours.ini'解析错误：{e}")  
    input("按下 任意键退出...")
    exit()  
 
 
# 初始化一个字典来存储结果  
attendance_report_temp = {} 
attendance_report_name = {} 

for name in names:  
    
    attendance_report_name[name] = {'出勤天数': 0.0, '漏打卡': 0, '上午迟到': 0, '上午早退': 0, '下午迟到': 0, '下午早退': 0}   
    # 筛选当前姓名的数据  
    person_data = df[df['姓名'] == name]  
    # print(person_data) 
    for date_col in date_columns: 
        if not person_data[date_col].dropna().empty:  
            str_times = person_data[date_col].dropna().iloc[0]          
            # 使用split('\n')按换行符分割字符串  
            times_list = str_times.split('\n')                
            # 转换每个时间字符串为datetime.time对象  
            time_objects = [datetime.strptime(time_str.strip(), "%H:%M").time() for time_str in times_list if time_str.strip()]  
            # 存储结果  
            attendance_report_temp = check_attendance(time_objects)
 
            attendance_report_name[name]['出勤天数'] += attendance_report_temp['出勤天数']
            attendance_report_name[name]['漏打卡']   += attendance_report_temp['漏打卡']
            attendance_report_name[name]['上午迟到'] += attendance_report_temp['上午迟到']
            attendance_report_name[name]['上午早退'] += attendance_report_temp['上午早退']
            attendance_report_name[name]['下午迟到'] += attendance_report_temp['下午迟到']
            attendance_report_name[name]['下午早退'] += attendance_report_temp['下午早退']
            
            #当天迟到次数
            temp_work_late = attendance_report_temp['上午迟到']  + attendance_report_temp['下午迟到']
            #当天早退次数
            temp_leave_early = attendance_report_temp['上午早退']+attendance_report_temp['下午早退']
 
            row_current = person_data.index[0] + 2  # Excel行号（Pandas索引从0开始，Excel行号从1开始）  
            col_current = date_columns.index(date_col) + 4  # Excel列号（同样，索引从0开始，列号从1开始）  
            cell_current = ws.cell(row=row_current, column=col_current) 
            # print(cell_current)
            
            if attendance_report_temp['漏打卡'] != 0:       #当天漏打卡不为0标记红色
                fill = PatternFill(start_color="FFFF0000", end_color="FFFF0000", fill_type="solid")  
                cell_current.fill = fill     
            elif attendance_report_temp['出勤天数'] == 0.5:       #当天出勤天数为0.5 标记黄色 
                fill = PatternFill(start_color="FFFFFF00", end_color="FFFFFF00", fill_type="solid")  
                cell_current.fill = fill
            elif ((temp_work_late + temp_leave_early ) != 0): #当天迟到早退次数不为0 标记靛蓝色 
                fill = PatternFill(start_color="FF00B0F0", end_color="FF00B0F0", fill_type="solid")  
                cell_current.fill = fill  
        # else:  
            # print(f"There are no non-NA values in {date_col}.")

    #根据出勤天数来计算额度调休天数，标准是出勤六天允许调休一天。
    attendance_report_name[name]['额定调休'] = attendance_report_name[name]['出勤天数'] // 6 # //是向下取整的除号 
    #事假天数 = 总天数 - 出勤天数 - 额定调休天数
    attendance_report_name[name]['事假天数'] = days_columns - attendance_report_name[name]['出勤天数'] -attendance_report_name[name]['额定调休'] 
    if attendance_report_name[name]['事假天数'] < 0:
        attendance_report_name[name]['事假天数'] = 0
    
 
# 将字符串转换为datetime对象  
specific_datetime = datetime.strptime(specific_datetime_str, "%Y-%m-%d %H:%M:%S")  
  
# 获取当前时间  
current_datetime = datetime.now() 

# 假设这是你的基础文件名  
base_filename = "打卡统计表"  
  
# 获取当前时间，并格式化为YYYYMMDD_HHMMSS  
current_time = datetime.now().strftime("%Y%m%d_%H%M%S") 
# 提取原始文件的名称部分（不包含扩展名），这里假设文件名格式为"xxx.txt"  
# os.path.splitext用于分割文件名和扩展名  
latest_filename = os.path.splitext(latest_file)[0]  
# 构造完整的文件名  
complete_filename = f"{base_filename}_{latest_filename}-{current_time}.txt" 
with open(complete_filename, "w", encoding="utf-8") as file:
    #判断是否超试用期
    if current_datetime > specific_datetime:
        print("时间超出试用期")
        # 将内容写入文件  
        file.write("超过试用期，请联系管理员 VX： 15206597003")
    else:
        for name, report in attendance_report_name.items():  
            # 构造要写入文件的字符串  
            line = f"{name} 的出勤天数：{report['出勤天数']}天，额定调休天数：{report['额定调休']}天，事假天数：{report['事假天数']}天，漏打卡：{report['漏打卡']}次，上午迟到：{report['上午迟到']}次，上午早退：{report['上午早退']}次，下午迟到：{report['下午迟到']}次，下午早退：{report['下午早退']}次。\n"  
            # 将字符串写入文件  
            file.write(line)  
            
print("生成TXT格式分析文件OK！")  

complete_filename = f"{base_filename}_{latest_filename}-{current_time}.csv" 
# 打开（或创建）一个CSV文件用于写入  
with open(complete_filename, 'w', newline='', encoding='utf-8') as csvfile:  
    # 创建一个DictWriter对象  
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)  
  
    # 写入标题行  
    writer.writeheader()  
  
    # 遍历出勤报告数据，并写入CSV文件  
    for name, report in attendance_report_name.items():  
        # 将出勤信息的键名转换为CSV文件列名所需的格式  
        row = {  
            '姓名': name,  
            '出勤天数': report['出勤天数'],  
            '额定调休天数': report['额定调休'],  
            '事假天数': report['事假天数'],  
            '漏打卡次数': report['漏打卡'],  
            '上午迟到次数': report['上午迟到'],  
            '上午早退次数': report['上午早退'],  
            '下午迟到次数': report['下午迟到'],  
            '下午早退次数': report['下午早退'],  
        }  
        writer.writerow(row)   
print("生成Excel格式分析表格OK！")  
# 保存更改  

cell_current = ws.cell(row=2, column=39) 
cell_current.value = '当天漏打卡'  
fill = PatternFill(start_color="FFFF0000", end_color="FFFF0000", fill_type="solid")  
cell_current.fill = fill  

cell_current = ws.cell(row=3, column=39) 
cell_current.value = '当天只出勤0.5天'  
fill = PatternFill(start_color="FFFFFF00", end_color="FFFFFF00", fill_type="solid")  
cell_current.fill = fill  

cell_current = ws.cell(row=4, column=39) 
cell_current.value = '当天迟到早退'  
fill = PatternFill(start_color="FF00B0F0", end_color="FF00B0F0", fill_type="solid")  
cell_current.fill = fill  


wb.save(f'-{month_without_zero_str}月表格着色标注副本_.xlsx')            
print("着色文件生成OK !")
